from django.shortcuts import render
from rest_framework.generics import GenericAPIView
from django_redis import get_redis_connection
from rest_framework.response import Response
from rest_framework import status
import pickle
import base64

from carts import constants
from .serializers import CartSerializer, CartSKUSerializer, CartDeleteSerializer, CartSelectAllSerializer
from goods.models import SKU


# Create your views here.


class CartView(GenericAPIView):
    """
    购物车
    """
    serializer_class = CartSerializer

    def perform_authentication(self, request):
        """
        重写父类的用户验证方法，不在进入视图前就检查JWT
        :param request:
        :return:
        """
        pass

    def post(self, request):
        """
        给购物车添加商品
        :param request:
        :return:
        """
        # 1 获取,校验参数
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 2 取出参数
        sku_id = serializer.validated_data['sku_id']
        count = serializer.validated_data['count']
        selected = serializer.validated_data['selected']

        # 3 判断用户登录状态
        try:
            user = request.user
        except Exception:
            user = None

        # 4 if 用户存在 把数据保存到redis中
        if user and user.is_authenticated:
            redis_conn = get_redis_connection('cart')
            pl = redis_conn.pipeline()

            # 以hash保存了商品id, 商品数量
            pl.hincrby('cart_%s' % user.id, sku_id, count)
            # 以set保存是否勾选
            if selected:
                pl.sadd('cart_selected_%s' % user.id, sku_id)
            pl.execute()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        # 5 else 用户不存在 把数据保存到cookie中
        else:
            # 从cookie中取出购物车数据
            str_cart = request.COOKIES.get('cart')  # 字符串
            # if 有值取出数据把字符串转化为字典
            if str_cart:
                str_cart = str_cart.encode()  # 字节
                str_byte = base64.b64decode(str_cart)  # 字节
                cart_dict = pickle.loads(str_byte)  # 字典
            # cart = pickle.loads(base64.b64decode(cart.encode()))
            # else 没值设置一个空字典
            else:
                cart_dict = {}

            # if 商品id在购物车内count累加, selected设置为cookie中取出来的值
            if sku_id in cart_dict:
                cart_dict[sku_id]['count'] += count
                cart_dict[sku_id]['selected'] = selected

            # else 商品id没在购物车内, 增加到字典中
            else:
                cart_dict[sku_id] = {
                    'count': count,
                    'selected': selected
                }
                # 把数据编码为字符串
            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            # cookie_cart = base64.b64encode(pickle.dumps(cart)).decode()

            # response 数据
            response = Response(serializer.data, status=status.HTTP_201_CREATED)
            # 给response设置cookie
            response.set_cookie('cart', cart_str, max_age=constants.CART_COOKIE_EXPIRES)
            # return response
            return response

    def get(self, request):
        """
        获取购物车数据
        :param request:
        :return:
        """
        # 1 判断用户时候存在
        try:
            user = request.user
        except Exception:
            user = None

        # 2 if 用户存在从redis中取出数据
        # 如果用户已登录，从redis中查询  sku_id  count selected
        if user and user.is_authenticated:
            reids_conn = get_redis_connection('cart')
            redis_cart = reids_conn.hgetall('cart_%s' % user.id)  # 返回一个字典
            # redis_cart = {
            #     商品的sku_id  bytes字节类型: 数量  bytes字节类型
            #     商品的sku_id  bytes字节类型: 数量  bytes字节类型
            #    ...
            # }
            redis_cart_selected = reids_conn.smembers('cart_selected_%s' % user.id)
            # redis_cart_selected = set(勾选的商品sku_id bytes字节类型, ....)

            cart_dict = {}
            # 遍历 redis_cart，形成cart_dict
            for sku_id, count in redis_cart.items():  # 取出的为byte类型
                cart_dict[int(sku_id)] = {
                    'count': int(count),
                    'selected': sku_id in redis_cart_selected
                }

        # 3 else 用户没登录从cookies中取出数据
        else:
            cookie_cart = request.COOKIES.get('cart')  # 字符串
            # 把字符串转为字典类型
            if cookie_cart:
                # 表示cookie中有购物车数据
                # 解析
                cookie_cart_bytes = cookie_cart.encode()  # 字节
                cookie_cart_bytes = base64.b64decode(cookie_cart_bytes)  # 字节
                cart_dict = pickle.loads(cookie_cart_bytes)  # 字典
                # cart_dict = pickle.loads(base64.b64decode(cookie_cart.encode()))
            else:
                # 表示cookie中没有购物车数据
                cookie_cart = {}

                # cart_dict = {
                #     sku_id_1: {
                #         'count': 10
                #         'selected': True
                #     },
                #     sku_id_2: {
                #         'count': 10
                #         'selected': False
                #     },
                # }

        # 查询数据库
        sku_id_list = cart_dict.keys()
        sku_obj_list = SKU.objects.filter(id__in=(sku_id_list))

        # 遍历sku_obj_list 向sku对象中添加count和selected属性
        for sku in sku_obj_list:
            sku.count = cart_dict[sku.id]['count']
            sku.selected = cart_dict[sku.id]['selected']

        serializer = CartSKUSerializer(instance=sku_obj_list, many=True)
        return Response(serializer.data)

    def put(self, request):
        """
        购物车内商品的修改
        :param request:
        :return:
        """
        # 校验参数
        serialzer = self.get_serializer(data=request.data)
        serialzer.is_valid(raise_exception=True)

        # 取出参数
        sku_id = serialzer.validated_data['sku_id']
        count = serialzer.validated_data['count']
        selected = serialzer.validated_data['selected']

        # 判断用户是否登录
        try:
            user = request.user
        except Exception:
            user = None

        # if 用户登录从redis中取出数据
        if user and user.is_authenticated:
            redis_conn = get_redis_connection('cart')

            pl = redis_conn.pipeline()
            pl.hset('cart_%s' % user.id, sku_id, count)

            # 是否勾选
            if selected:
                pl.sadd('cart_selected_%s' % user.id, sku_id)
            else:
                pl.srem('cart_selected_%s' % user.id, sku_id)
            pl.execute()

            return Response(serialzer.data, status=status.HTTP_201_CREATED)

        # 用户未登录
        else:
            cart = request.COOKIES.get('cart')

            if cart is not None:
                cart_dict = pickle.loads(base64.b64decode(cart.encode()))
            else:
                cart_dict = {}

            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }
            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()

            response = Response(serialzer.data, status=status.HTTP_201_CREATED)
            response.set_cookie('cart', cart_str, constants.CART_COOKIE_EXPIRES)

            return response

    def delete(self, request):
        """
        购物车删除数据
        :param request:
        :return:
        """
        serializer = CartDeleteSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        sku_id = serializer.validated_data['sku_id']

        try:
            user = request.user
        except Exception:
            user = None

        if user and user.is_authenticated:
            redis_conn = get_redis_connection('cart')

            pl = redis_conn.pipeline()
            pl.hdel('cart_%s' % user.id, sku_id)
            pl.srem('cart_selected_%s' % user.id, sku_id)
            pl.execute()
            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            # 用户未登录，在cookie中保存
            response = Response(status=status.HTTP_204_NO_CONTENT)

            cart = request.COOKIES.get('cart')
            if cart is not None:
                cart_dict = pickle.loads(base64.b64decode(cart.encode()))
                if sku_id in cart_dict:
                    del cart_dict[sku_id]
                    cookie_cart = base64.b64encode(pickle.dumps(cart)).decode()
                    # 设置购物车的cookie
                    # 需要设置有效期，否则是临时cookie
                    response.set_cookie('cart', cookie_cart, max_age=constants.CART_COOKIE_EXPIRES)

            return response


class CartSelectAllView(GenericAPIView):
    """
    购物车全选
    """

    def perform_authentication(self, request):
        """
        重写父类的用户验证方法，不在进入视图前就检查JWT
        """
        pass

    def put(self, request):
        """
        购物车是否全选
        :param request:
        :return:
        """
        serializer = CartSelectAllSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        selected = serializer.validated_data['selected']

        try:
            user = request.user
        except Exception:
            user = None

        if user and user.is_authenticated:
            redis_conn = get_redis_connection('cart')
            cart_dict = redis_conn.hgetall('cart_%s' % user.id)
            sku_id_list = cart_dict.keys()

            if selected:
                redis_conn.sadd('cart_selected_%s' % user.id, *sku_id_list)
            else:
                redis_conn.srem('cart_selected_%s' % user.id, *sku_id_list)

            return Response({'message': 'OK'})
        else:
            response = Response({'message': 'OK'})
            cart = request.COOKIES.get('cart')
            if cart:
                cart_dict = pickle.loads(base64.b64decode(cart.encode()))

                for sku_id in cart_dict:
                    cart_dict[sku_id]['selected'] = selected

                    cookie_cart = base64.b64encode(pickle.dumps(cart)).decode()
                    # 设置购物车的cookie
                    # 需要设置有效期，否则是临时cookie
                    response.set_cookie('cart', cookie_cart, max_age=constants.CART_COOKIE_EXPIRES)
            return response
